/*
 ------------------------------------------------------------------------------
 Copyright (C) 1996-2001 Id Software, Inc.

 This file is part of the Quake source code.

 The Quake source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quake source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quake source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// math.cpp - math library
//


#include "../shared.h"
#include "anorms.h"


/*
 ==================
 glqMath::FloatToHalf
 ==================
*/
half glqMath::FloatToHalf (float f){

	int		s, e, m;
	int		i, h;

	i = *(int *)&f;

	s = (i >> 16) & 0x00008000;
	e = ((i >> 23) & 0x000000FF) - 112;
	m = i & 0x007FFFFF;

	if (e <= 0)
		h = s | ((((m | 0x00800000) >> (1 - e)) + 0x00001000) >> 13);
	else if (e == 255 - 112){
		if (m == 0)
			h = s | 0x00007C00;
		else {
			m >>= 13;

			h = (s | 0x00007C00) | (m | (m == 0));
		}
	}
	else {
		m += 0x00001000;
		if (m & 0x00800000){
			e++;
			m = 0;
		}

		if (e >= 31)
			h = s | 0x00007C00;
		else
			h = s | (e << 10) | (m >> 13);
	}

	return h;
}

/*
 ==================
 glqMath::HalfToFloat
 ==================
*/
float glqMath::HalfToFloat (half h){

	int		s, e, m;
	int		i;

	s = (h & 0x00008000) << 16;
	e = (h >> 10) & 0x0000001F;
	m = h & 0x000003FF;

	if (e == 0){
		if (m == 0)
			i = s;
		else {
			while (!(m & 0x00000400)){
				e--;
				m += m;
			}

			i = s | (((e + 1) + 112) << 23) | ((m & ~0x00000400) << 13);
		}
	}
	else if (e == 31)
		i = (s | 0x7F800000) | (m << 13);
	else
		i = s | ((e + 112) << 23) | (m << 13);

	return *(float *)&i;
}

/*
 ==================
 glqMath::NormalToBytes
 ==================
*/
void glqMath::NormalToBytes (const glqVec3 &normal, byte bytes[2]){

	int		lat, lng;

	// Check for singularities
	if (normal.x == 0.0f && normal.y == 0.0f){
		if (normal.z > 0.0f){
			bytes[0] = 0;
			bytes[1] = 0;
		}
		else {
			bytes[0] = 128;
			bytes[1] = 0;
		}

		return;
	}

	lat = FloatToInt(RAD2DEG(ATan(normal.y, normal.x)) * (255.0f / 360.0f));
	lng = FloatToInt(RAD2DEG(ACos(normal.z)) * (255.0f / 360.0f));

	bytes[0] = lng & 0xFF;
	bytes[1] = lat & 0xFF;
}

/*
 ==================
 glqMath::BytesToNormal
 ==================
*/
void glqMath::BytesToNormal (const byte bytes[2], glqVec3 &normal){

	float	lat, lng;
	float	sLat, cLat, sLng, cLng;

	lng = (float)(bytes[0]) * (M_PI / 128.0f);
	lat = (float)(bytes[1]) * (M_PI / 128.0f);

	SinCos(lat, sLat, cLat);
	SinCos(lng, sLng, cLng);

	normal.x = sLng * cLat;
	normal.y = sLng * sLat;
	normal.z = cLng;
}

/*
 ==================
 glqMath::ByteToDir
 ==================
*/
void glqMath::ByteToDir (int b, glqVec3 &dir){

	if (b < 0 || b >= NUM_VERTEX_NORMALS){
		dir.Clear();
		return;
	}

	dir[0] = m_byteDirs[b][0];
	dir[1] = m_byteDirs[b][1];
	dir[2] = m_byteDirs[b][2];
}